HELP='''
功能：修改文件时间属性（创建、修改、访问）
运行方法：
python mofify_file_time.py 文件或目录 参数 值
可选参数：
--atime 访问时间
--ctime 创建时间
--mtime 修改时间
--alltime 以上三个全部
值格式(用引号包起来)：
'20240113 12:31:12'

可选参数：
--adayDelta 访问时间日期偏移
--cdayDelta 创建时间日期偏移
--mdayDelta 修改时间日期偏移
--alldayDelta 以上三个全部修改
值格式：
20240113，将当前文件时间偏移到20240113，时分秒不变

--from 文件路径  从给定文件获取要修改的时间
'''

import time, os, sys
from datetime import datetime, timedelta
import platform
if platform.system() == 'Windows':
    from win32file import CreateFile, SetFileTime, GetFileTime, CloseHandle
    from win32file import GENERIC_READ, GENERIC_WRITE, OPEN_EXISTING
    from pywintypes import Time # 可以忽视这个 Time 报错（运行程序还是没问题的）
import random

from PIL import Image
from PIL.ExifTags import TAGS
import piexif


def get_photo_time(photo_path):
    image = Image.open(photo_path)
    exif_data = image._getexif()
    try:
        for tag, value in exif_data.items():
            tag_name = TAGS.get(tag)
            if tag_name == 'DateTimeOriginal':
                print("拍摄时间", value)
                return value
    except:
        pass

def modify_photo_time(photo_path, new_time):
    # image = Image.open(photo_path)
    # exif_data = image._getexif()
    # try:
    #     # 方法一
    #     image.info['datetime'] = new_time
    #     image.save(photo_path)
    #     print("拍摄时间修改成功", new_time, type(new_time))
    #     # 方法二
    #     # for tag, value in exif_data.items():
    #     #     tag_name = TAGS.get(tag)
    #     #     if tag_name == 'DateTimeOriginal':
    #     #         exif_data[tag] = new_time
    #     #         image.save(photo_path)
    #     #         print("拍摄时间修改成功！", new_time)
    #     #         break
    # except:
    #     print("拍摄时间修改失败")

    # 方法三，因为上面的方法都不行
    exif_dict = piexif.load(photo_path)
    # print(exif_dict)
    # 修改EXIF中的拍摄时间
    exif_dict['Exif'][piexif.ExifIFD.DateTimeOriginal] = new_time
    exif_dict['Exif'][piexif.ExifIFD.DateTimeDigitized] = new_time
    if 41729 in exif_dict['Exif']:
        # 不知道这个值是什么意思，但是如果它是int类型就报错
        exif_dict['Exif'][41729] = bytes(exif_dict['Exif'][41729])
    # print("新的拍摄时间", new_time, exif_dict)
    piexif.insert(piexif.dump(exif_dict), photo_path)

# 使用示例
# photo_path = 'example.jpg'
# new_time = "2022:01:01 12:00:00"
# modify_photo_time(photo_path, new_time)


def get_file_time(path, print_info=""):
    create = os.path.getctime(path) # 返回时间戳
    modify = os.path.getmtime(path)
    access = os.path.getatime(path)
    print(f"文件{path}{print_info}的时间属性：")
    print("创建", create, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(create)))
    print("修改", modify, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(modify)))
    print("访问", access, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(access)))
    return create, modify, access


def change_file_time(path, ctime=None, mtime=None, atime=None,
                 cdayDelta=None, mdayDelta=None, adayDelta=None):
        '''
            mtime: 修改到此参数指定的时间戳
            mdayDelta: 修改到此参数指定的日期，时分秒不变
        '''
        # 1. 获取旧时间
        create, modify, access = get_file_time(path, "修改前")
        # 如果是图片且有拍摄时间，旧修改时间以拍摄时间（时分秒）为准
        photo_time_fmt = "%Y:%m:%d %H:%M:%S"
        is_photo = False
        if path.endswith(".jpg"):
            photo_time = get_photo_time(path)
            if photo_time:
                modify = datetime.strptime(photo_time, photo_time_fmt).timestamp() + round(random.random(), 7) 
                is_photo = True
        
        # 2. 计算新时间
        # 按日期偏移
        new_create, new_modify, new_access = create, modify, access
        if cdayDelta:
            cdayDelta = (datetime.fromtimestamp(create)-cdayDelta).days
            new_create = (datetime.fromtimestamp(create) - timedelta(days=cdayDelta)).timestamp()
        if mdayDelta:
            mdayDelta = (datetime.fromtimestamp(modify)-mdayDelta).days
            new_modify = (datetime.fromtimestamp(modify) - timedelta(days=mdayDelta)).timestamp()
        if adayDelta:
            adayDelta = (datetime.fromtimestamp(access)-adayDelta).days
            new_access = (datetime.fromtimestamp(access) - timedelta(days=adayDelta)).timestamp()
        # 明确指定时间
        if mtime:
            new_modify = mtime
        if ctime:
            new_create = ctime
        if atime:
            new_access = atime

        # 3. 修改时间
        # 修改拍摄时间 为你要的 新修改时间
        if is_photo:
            try:
                modify_photo_time(path, datetime.fromtimestamp(new_modify).strftime(photo_time_fmt))
            except:
                print("修改此图片拍摄日期出错", path)

        # 修改文件时间
        
        # 方法一
        if platform.system() == 'Windows':
            fh = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, None, OPEN_EXISTING, 0, 0)
            # create, access, modify = GetFileTime(fh)
            # print(f"文件【{file}】时间为:", create, access, modify)
            SetFileTime(fh, Time(new_create), Time(access), Time(modify))  # 修改创建时间，不支持毫秒至纳秒位修改
            CloseHandle(fh)
        # 方法二
        os.utime(path, (new_access, new_modify)) # 修改访问时间和修改时间，支持毫秒至纳秒，但无法修改创建时间
        # 获取新时间
        get_file_time(path, "修改后")
        if is_photo:
            get_photo_time(path)
        print("\n")


def parse_argv(arg):
    if arg in sys.argv:
        return sys.argv[sys.argv.index(arg)+1]


def main():
    if len(sys.argv)==1 or '-h' in sys.argv or '-help' in sys.argv:
        print(HELP)
        return
    # 明确的修改时间
    params = {
        "ctime": None,
        "atime": None,
        "mtime": None,
    }
    for key in params:
        fmt = '%Y%m%d %H:%M:%S' # 给定的时间格式必须为20240315 15:25:00
        ns = round(random.random(), 7) # 纳秒位至毫秒位随机
        val = parse_argv('--'+key)
        if not val:
            val = parse_argv('--alltime')
        if val:
            params[key] = datetime.strptime(val, fmt).timestamp() + ns

    # 偏移修改时间
    params2 = {
        "cdayDelta":None,
        "adayDelta":None,
        "mdayDelta":None
    }
    for key in params2:
        fmt = '%Y%m%d' # 给定的时间格式必须为20240315
        val = parse_argv('--'+key)
        if not val:
            val = parse_argv('--alldayDelta')
        if val:
            params2[key] = datetime.strptime(val, fmt)
    
    # 从文件获取时间
    source_file = parse_argv('--from')
    if source_file:
        create, modify, access = get_file_time(source_file)
        params['ctime'], params["atime"], params["mtime"] = create, access, modify

    params.update(params2)
    print(params)

    if len(sys.argv)>1:
        path = sys.argv[1]
        if not os.path.exists(path):
            print("给定路径不存在")
            return
        if os.path.isfile(path):
            change_file_time(path, **params)
        # 检查给定目录
        else:
            for cur, dirs, files in os.walk(path):
                for file in files:
                    change_file_time(os.path.join(cur, file), **params)

    print("finished!")

if __name__ == '__main__':
    main()